Skip to content

Capsule foremanctl certificate bundle deploy#21325

Open
amolpati30 wants to merge 1 commit into
SatelliteQE:masterfrom
amolpati30:Capsule_certificate_bundle_deploy
Open

Capsule foremanctl certificate bundle deploy#21325
amolpati30 wants to merge 1 commit into
SatelliteQE:masterfrom
amolpati30:Capsule_certificate_bundle_deploy

Conversation

@amolpati30

@amolpati30 amolpati30 commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

Validate that the foremanctl certificate-bundle command successfully generates a valid certificate bundle tarball for a given Capsule hostname. The validation ensures that the generated tarball contains all required certificate files, including CA, server, and client certificates, and verifies the integrity and consistency of duplicated certificate pairs by confirming they are byte-identical.

Summary by Sourcery

Add automated validation of foremanctl certificate bundle generation and renewal for Capsules using both default and custom server certificates.

Tests:

  • Introduce capsule_certificate_bundle pytest fixture to provision Capsule certificate bundles from either default or custom server certificates and verify the resulting tarball exists.
  • Add an end-to-end foremanctl certificate bundle test that checks initial certificate validity, Satellite renewal effects, and subsequent Capsule bundle renewal for both Satellite and Capsule hosts.

@amolpati30 amolpati30 requested review from a team as code owners April 15, 2026 07:12
@amolpati30 amolpati30 added No-CherryPick PR doesnt need CherryPick to previous branches Stream Introduced in or relating directly to Satellite Stream/Master labels Apr 15, 2026
@sourcery-ai

sourcery-ai Bot commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Adds a new parameterized test and shared fixture to validate foremanctl certificate-bundle behavior for Capsule hosts, ensuring certificate tarball creation and correct certificate validity after renewals for both default and custom server certificate sources.

Sequence diagram for foremanctl certificate-bundle fixture behavior

sequenceDiagram
    participant Test as pytest
    participant Sat as sat
    participant Capsule as rhel_contenthost

    Test->>Sat: install_satellite_foremanctl(parameters)
    alt certificate_source == custom_server
        Test->>Sat: custom_cert_generate(sat.hostname)
        Test->>Sat: install_satellite_foremanctl(parameters=[--certificate-source=custom_server])
        Test->>Sat: execute("foremanctl certificate-bundle --certificate-server-certificate /root/hostname/hostname.crt --certificate-server-key /root/hostname/hostname.key", Capsule, timeout="30m")
        Sat-->>Test: deploy (status)
        Test->>Sat: execute("tar tzf /var/lib/foremanctl/certs/bundles/rhel_contenthost.tar.gz")
        Sat-->>Test: result (status)
    else certificate_source == default
        Test->>Sat: install_satellite_foremanctl()
        Test->>Sat: execute("foremanctl certificate-bundle rhel_contenthost", timeout="30m")
        Sat-->>Test: deploy (status)
        Test->>Sat: execute("tar tzf /var/lib/foremanctl/certs/bundles/rhel_contenthost.tar.gz")
        Sat-->>Test: result (status)
    end

    Test-->>Test: assert deploy.status == 0
    Test-->>Test: assert result.status == 0
Loading

File-Level Changes

Change Details Files
Introduce helper utilities and a new end-to-end test that validates certificate validity days for Satellite and Capsule certificates across initial deployment and renewal flows, including Capsule certificate bundle renewals.
  • Add CAPSULE_CERTS_DIR constant and helper functions to build Satellite and Capsule certificate paths used in tests
  • Add assert_cert_validity_days helper to DRY certificate-validity checks across multiple cert paths
  • Define DEFAULT_CERT_DAYS and RENEWED_CERT_DAYS to encode expected validity windows for initial and renewed certificates
  • Add test_positive_foremanctl_certificate_bundle parametrized on certificate source to verify initial cert validity, Satellite-only renewal, and Capsule bundle renewal behaviors
tests/foreman/installer/test_install_foremanctl.py
Add a reusable pytest fixture that prepares a Satellite instance and Capsule host for certificate-bundle testing, supporting both default and custom server certificate sources and verifying that the foremanctl-generated bundle tarball exists and is readable.
  • Create capsule_certificate_bundle fixture scoped to module that wires in module_sat_ready_rhel and rhel_contenthost
  • Implement fixture logic for the custom_server path, including generating custom certs, installing Satellite with custom_server source, running foremanctl certificate-bundle with explicit server cert/key, and verifying the resulting tarball contents with tar tzf
  • Implement fixture logic for the default certificate source path, installing Satellite with default settings, invoking foremanctl certificate-bundle with the Capsule hostname, and verifying the bundle tarball exists and is listable
  • Return (sat, rhel_contenthost) from the fixture to be consumed by tests
pytest_fixtures/component/foremanctl.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@amolpati30 amolpati30 force-pushed the Capsule_certificate_bundle_deploy branch from 867559d to 36990ca Compare April 15, 2026 07:14

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 security issues, 3 other issues, and left some high level feedback:

Security issues:

  • Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option. (link)
  • Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option. (link)

General comments:

  • In install_capsule_foremanctl, cer_source and hostname are required for the foremanctl certificate-bundle command but default to None and are never passed from the fixture, which will cause a failure when " ".join(cer_source) is called; consider making them required parameters and validating them before use.
  • The new upstream-specific branch in setup_capsule_repos is not used by install_capsule_foremanctl, which currently calls setup_satellite_repos; verify whether Capsule installs should instead call setup_capsule_repos so the upstream Capsule repo setup logic is actually exercised.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `install_capsule_foremanctl`, `cer_source` and `hostname` are required for the `foremanctl certificate-bundle` command but default to `None` and are never passed from the fixture, which will cause a failure when `" ".join(cer_source)` is called; consider making them required parameters and validating them before use.
- The new upstream-specific branch in `setup_capsule_repos` is not used by `install_capsule_foremanctl`, which currently calls `setup_satellite_repos`; verify whether Capsule installs should instead call `setup_capsule_repos` so the upstream Capsule repo setup logic is actually exercised.

## Individual Comments

### Comment 1
<location path="robottelo/hosts.py" line_range="2041-2050" />
<code_context>
+        cer_source=None,
</code_context>
<issue_to_address>
**issue (bug_risk):** Handle `cer_source` more defensively before joining it into the command.

`cer_source` can be `None`, but later you call `" ".join(cer_source)`, which will fail if it’s `None` or not an iterable of strings. Either make `cer_source` required (and document it as a list/tuple of strings), or add validation and raise a clear error if it’s missing/invalid.
</issue_to_address>

### Comment 2
<location path="robottelo/hosts.py" line_range="2042-2051" />
<code_context>
+        hostname=None,
</code_context>
<issue_to_address>
**issue (bug_risk):** Avoid passing a `None` hostname through to the `foremanctl certificate-bundle` command.

If `hostname` remains `None`, the command will literally include `None`, which is likely invalid and hard to diagnose. Please either require `hostname` or validate it before constructing the command and fail fast with a clear error if it’s missing or empty.
</issue_to_address>

### Comment 3
<location path="tests/foreman/installer/test_install_foremanctl.py" line_range="211" />
<code_context>
+    capsule = module_capsule_foremanctl_custom_certs
+    common_sat_install_assertions(capsule)
+    # check the services are up and healthy
+    result = capsule.execute('hammer ping')
+    assert result.status == 0
+    assert result.stdout.count('Status:') == result.stdout.count(' ok')
</code_context>
<issue_to_address>
**issue (testing):** The test does not assert that the served certificate CN matches the Capsule hostname as described in the PR.

This test only checks `hammer ping` and an HTTPS 200 with the custom CA. Please add an assertion that inspects the server certificate CN (for example via `openssl s_client` or `curl --resolve` with verbose output) and verifies it equals `capsule.hostname`, so the test confirms the expected certificate is actually presented for that hostname.
</issue_to_address>

### Comment 4
<location path="robottelo/hosts.py" line_range="2079-2082" />
<code_context>
            self.execute(
                f'foremanctl certificate-bundle --certificate-source {" ".join(cer_source)} {hostname} ',
                timeout='30m',
            ).status
</code_context>
<issue_to_address>
**security (python.sqlalchemy.security.sqlalchemy-execute-raw-query):** Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option.

*Source: opengrep*
</issue_to_address>

### Comment 5
<location path="robottelo/hosts.py" line_range="2096-2099" />
<code_context>
            self.execute(
                f'foremanctl deploy {" ".join(default_parameters)}',
                timeout='30m',
            ).status
</code_context>
<issue_to_address>
**security (python.sqlalchemy.security.sqlalchemy-execute-raw-query):** Avoiding SQL string concatenation: untrusted input concatenated with raw SQL query can result in SQL Injection. In order to execute raw query safely, prepared statement should be used. SQLAlchemy provides TextualSQL to easily used prepared statement with named parameters. For complex SQL composition, use SQL Expression Language or Schema Definition Language. In most cases, SQLAlchemy ORM will be a better option.

*Source: opengrep*
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread robottelo/hosts.py Outdated
Comment thread robottelo/hosts.py Outdated
Comment thread tests/foreman/installer/test_install_foremanctl.py Outdated
Comment thread robottelo/hosts.py Outdated
Comment thread robottelo/hosts.py Outdated

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New security issues found

Comment thread robottelo/hosts.py Outdated
Comment thread robottelo/hosts.py Outdated
@amolpati30 amolpati30 force-pushed the Capsule_certificate_bundle_deploy branch 5 times, most recently from 08c11c5 to d87d52d Compare April 16, 2026 07:02
@amolpati30 amolpati30 changed the title Add Capsule foremanctl test to deploy with custom certs Capsule foremanctl certificate bundle deploy Apr 16, 2026
@amolpati30 amolpati30 force-pushed the Capsule_certificate_bundle_deploy branch 3 times, most recently from 664d2f7 to 046d3f1 Compare April 16, 2026 08:41
@Satellite-QE

Copy link
Copy Markdown
Collaborator

PRT Result

Build Number: 15147
Build Status: UNSTABLE
PRT Comment: pytest tests/foreman/installer/test_install_foremanctl.py -k test_positive_foremanctl_certificate_bundle_generation --external-logging
Test Result : ========== 1 failed, 15 deselected, 10 warnings in 2183.24s (0:36:23) ==========

@Satellite-QE Satellite-QE added the PRT-Failed Indicates that latest PRT run is failed for the PR label Apr 16, 2026
@amolpati30 amolpati30 force-pushed the Capsule_certificate_bundle_deploy branch from 046d3f1 to 71c5be0 Compare April 16, 2026 17:15
@amolpati30 amolpati30 marked this pull request as draft April 16, 2026 17:16
@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown

This pull request has not been updated in the past 45 days.

@github-actions github-actions Bot added the Stale Stale issue or Pull Request label Jun 1, 2026
@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown

This pull request is now being closed after stale warnings.

@github-actions github-actions Bot closed this Jun 9, 2026
@amolpati30 amolpati30 reopened this Jun 9, 2026
@github-actions github-actions Bot removed the Stale Stale issue or Pull Request label Jun 10, 2026
@amolpati30 amolpati30 closed this Jun 22, 2026
@amolpati30 amolpati30 force-pushed the Capsule_certificate_bundle_deploy branch from 71c5be0 to 00fd5d6 Compare June 22, 2026 07:51
@amolpati30 amolpati30 force-pushed the Capsule_certificate_bundle_deploy branch 4 times, most recently from 5f90cfb to 5cba060 Compare July 1, 2026 07:53
@Satellite-QE

Copy link
Copy Markdown
Collaborator

PRT Result

Build Number: 15997
Build Status: UNSTABLE
PRT Comment: pytest tests/foreman/installer/test_install_foremanctl.py -k test_positive_foremanctl_certificate_bundle --external-logging
Test Result : ========== 1 failed, 20 deselected, 11 warnings in 2124.66s (0:35:24) ==========

@amolpati30 amolpati30 force-pushed the Capsule_certificate_bundle_deploy branch from 5cba060 to b6239b5 Compare July 1, 2026 09:27
@Satellite-QE

Copy link
Copy Markdown
Collaborator

PRT Result

Build Number: 16000
Build Status: UNSTABLE
PRT Comment: pytest tests/foreman/installer/test_install_foremanctl.py -k test_positive_foremanctl_certificate_bundle --external-logging
Test Result : ========== 1 failed, 20 deselected, 11 warnings in 2151.22s (0:35:51) ==========

@amolpati30 amolpati30 force-pushed the Capsule_certificate_bundle_deploy branch from b6239b5 to 02a85f2 Compare July 1, 2026 10:35
@Satellite-QE

Copy link
Copy Markdown
Collaborator

PRT Result

Build Number: 16002
Build Status: UNSTABLE
PRT Comment: pytest tests/foreman/installer/test_install_foremanctl.py -k test_positive_foremanctl_certificate_bundle --external-logging
Test Result : ========== 1 failed, 20 deselected, 11 warnings in 2163.85s (0:36:03) ==========

@amolpati30 amolpati30 force-pushed the Capsule_certificate_bundle_deploy branch from 02a85f2 to 4743166 Compare July 1, 2026 12:20
@Satellite-QE

Copy link
Copy Markdown
Collaborator

PRT Result

Build Number: 16005
Build Status: UNSTABLE
PRT Comment: pytest tests/foreman/installer/test_install_foremanctl.py -k test_positive_foremanctl_certificate_bundle --external-logging
Test Result : ========== 1 failed, 20 deselected, 11 warnings in 2163.43s (0:36:03) ==========

@amolpati30 amolpati30 force-pushed the Capsule_certificate_bundle_deploy branch 2 times, most recently from bcc88fe to 67bd144 Compare July 1, 2026 13:59

@ShimShtein ShimShtein left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Satellite-QE

Copy link
Copy Markdown
Collaborator

PRT Result

Build Number: 16009
Build Status: UNSTABLE
PRT Comment: pytest tests/foreman/installer/test_install_foremanctl.py -k test_positive_foremanctl_certificate_bundle --external-logging
Test Result : ========== 1 failed, 20 deselected, 11 warnings in 2330.73s (0:38:50) ==========

@amolpati30 amolpati30 force-pushed the Capsule_certificate_bundle_deploy branch from 67bd144 to b93df3c Compare July 2, 2026 11:53
@amolpati30

Copy link
Copy Markdown
Contributor Author
trigger: test-robottelo
pytest: tests/foreman/installer/test_install_foremanctl.py -k test_positive_foremanctl_certificate_bundle
theforeman:
    foremanctl: 90

@Satellite-QE

Copy link
Copy Markdown
Collaborator

PRT Result

Build Number: 16024
Build Status: SUCCESS
PRT Comment: pytest tests/foreman/installer/test_install_foremanctl.py -k test_positive_foremanctl_certificate_bundle --external-logging
Test Result : ========== 1 passed, 20 deselected, 11 warnings in 2475.22s (0:41:15) ==========

@Satellite-QE Satellite-QE added PRT-Passed Indicates that latest PRT run is passed for the PR and removed PRT-Failed Indicates that latest PRT run is failed for the PR labels Jul 2, 2026
@amolpati30 amolpati30 requested review from a team and evgeni July 2, 2026 13:04

# Phase 1: initial Satellite validity
assert_cert_validity_days(sat, sat_certs, DEFAULT_CERT_DAYS)
assert_cert_validity_days(sat, capsule_certs, DEFAULT_CERT_DAYS)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still does not validate the certificates inside the bundle.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that only verifies the bundle has a file called {hostname}.crt, not the content of the file

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already test the presence of the files in https://github.com/theforeman/foremanctl/blob/master/tests/certificate_bundle_test.py, no need to repeat that (IMHO)

The "did the bundle change (or not change) properly depending on user input" is a valuable test.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, this is already verified in the unit tests, so we can skip it.

@amolpati30 amolpati30 force-pushed the Capsule_certificate_bundle_deploy branch from b93df3c to 4882ed7 Compare July 7, 2026 11:09
@Satellite-QE Satellite-QE removed the PRT-Passed Indicates that latest PRT run is passed for the PR label Jul 7, 2026
@amolpati30 amolpati30 force-pushed the Capsule_certificate_bundle_deploy branch from 4882ed7 to 39933c1 Compare July 7, 2026 11:11
@amolpati30 amolpati30 force-pushed the Capsule_certificate_bundle_deploy branch from 39933c1 to be5bcd2 Compare July 7, 2026 13:25
@amolpati30

Copy link
Copy Markdown
Contributor Author
trigger: test-robottelo
pytest: tests/foreman/installer/test_install_foremanctl.py -k test_positive_foremanctl_certificate_bundle
theforeman:
    foremanctl: 90

@evgeni

evgeni commented Jul 8, 2026

Copy link
Copy Markdown
Member
16:10:48  collected 21 items / 21 deselected / 0 selected

Why is it deselecting all our tests?!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

No-CherryPick PR doesnt need CherryPick to previous branches Stream Introduced in or relating directly to Satellite Stream/Master

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants